#include <csignal>
#include "main.h"
#include "com_service.h"
#include "uart.h"
#include "inc.h"
#include "config.h"

#define VERSION_NUM "1.0.7"

bool quitting = false;
const char *ITC_FRAME_HEADER = "BF0701";
string g_dev_name = "";
string g_server_addr = "";
string g_server_url = "";
string g_station_id = "";
string g_term_id = "";

process_mgr_t process_mgr = nullptr;

static void signal_handler(int signum)
{
    switch (signum)
    {
        case SIGINT:
        case SIGTERM:
            printf("the thread will to quit...\n");
            if(!quitting) quitting = true;
            break;
        case SIGHUP:
            break;
        case SIGQUIT:
            printf("the thread will to quit...\n");
            if(!quitting) quitting = true;
            break;
        case SIGPIPE:
            printf("recv the signal SIGPIPE\n");
            break;
        case SIGUSR1: {
                printf("recv the signal SIGUSR1\n");
                int ret = zlog_reload("itc_log.conf");
                if (ret == 0) {
                    dzlog_info("zlog reload conf ok...");
                } else {
                    dzlog_warn("zlog reload conf failed:%d", ret);
                }
            }
            break;
        case SIGUSR2:{
                printf("recv the signal SIGUSR2\n");
                dzlog_info("to reload the config file");

                if(init_config()){
                    dzlog_info("reload the config file success");
                }
                else{
                    dzlog_error("reload the config file error");
                }
            }
            break;
        default:
            break;
    }
}

static void setup_signal_handlers()
{
    struct sigaction act;
    memset(&act, 0, sizeof(struct sigaction));

    act.sa_handler = signal_handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGINT,  &act, nullptr);
    sigaction(SIGTERM, &act, nullptr);
    sigaction(SIGQUIT, &act, nullptr);
    sigaction(SIGHUP,  &act, nullptr);
    sigaction(SIGPIPE, &act, nullptr);
    sigaction(SIGUSR1, &act, nullptr);
    sigaction(SIGUSR2, &act, nullptr);
}


int handle_com_event(int fd, const char *cmd, string & com_data)
{
    int ret = -1;
    process_mgr->cmd_type = (unsigned)strtol(cmd, nullptr, 16);
    if(strcasecmp(cmd, "C1") == 0){
        //do init
        ret = init(com_data);

    }else if(strcasecmp(cmd, "C2") == 0){

    }else if(strcasecmp(cmd, "C3") == 0){

        ret = get_pos_version(com_data);

    }else if(strcasecmp(cmd, "C4") == 0){

        ret = query_car_order_info(fd, com_data);

    }else if(strcasecmp(cmd, "C5") == 0){

        ret = pos_trans(com_data);

    }else if(strcasecmp(cmd, "C6") == 0){
        ret = pos_trans(com_data);

    }else if(strcasecmp(cmd, "C7") == 0){

    }else if(strcasecmp(cmd, "C8") == 0){

        ret = get_pos_status(com_data);

    }else if(strcasecmp(cmd, "C9") == 0){

    }else if(strcasecmp(cmd, "CA") == 0){

    }else{

    }

    return ret;
}

bool initialize()
{

    process_mgr = (process_mgr_t)malloc(sizeof(PROCESS_MGR_ST));
    memset(process_mgr, 0, sizeof(PROCESS_MGR_ST));

    if(process_mgr == nullptr)
        return false;
    int rc;
    rc = dzlog_init("itc_log.conf", "my_cat");
    if (rc) {
        printf("init log failed\n");
        return false;
    }

    curl_global_init(CURL_GLOBAL_ALL);

    return init_config();
}

void cleanup()
{
    zlog_fini();

    if(process_mgr == nullptr)
        return;

    curl_global_cleanup();

    free(process_mgr);
    process_mgr = nullptr;
}

void * handle_com_thread(void *arg)
{
    char *buff = (char *)(malloc(sizeof(char)*MAX_BUF_LEN));
    memset(buff, 0, MAX_BUF_LEN);
    int total = 0;
    volatile bool bhas_header = false;
    char *p_header = nullptr;
    if(process_mgr->com_fd <= 0){
        dzlog_error("error com fd :%d\n", process_mgr->com_fd);
        free(buff);
        return nullptr;
    }else{
        dzlog_debug("the com fd :%d\n", process_mgr->com_fd);
    }

    char *p_first_nonull = nullptr;
    volatile bool bneed_reopen = false;
    while (!quitting){
        ssize_t nread = 0;
        if(bneed_reopen){
            int fd = reopen_uart(g_dev_name.c_str(), process_mgr->com_fd);
            if(fd <= 0 ){
                dzlog_error("reopen uart error:%d,exit..", fd);
                quitting = true;
                continue;
            }else{
                process_mgr->com_fd = fd;
                dzlog_info("reopen uart %s success %d",g_dev_name.c_str(), fd);
                bneed_reopen = false;
                //init
                total = 0;
                memset(buff, 0, sizeof(char)*MAX_BUF_LEN);
                bhas_header = false;
                p_header = nullptr;
                p_first_nonull = nullptr;
            }
        }

        nread = read(process_mgr->com_fd, buff+total, MAX_BUF_LEN-total);
        if(nread < 0){
            if(errno == EAGAIN){
                continue;
            }
            dzlog_info("nread < 0 :%ld(%d)", nread, errno);
            break;
        }
        if(errno == EAGAIN || nread == 0){
            continue;
        }
        if(nread > 0){
            //dui shu ju jin xing yu chu li
//            if(nread == 1 && total == 1){
//                if(strlen(buff) == 0 || buff[0] != 66){
//                    bneed_reopen = true;
//                    dzlog_error("invaild com data,need to reopen uart(%02X)", buff[0]);
//                    continue;
//                }
//            }
            total += nread;
            if(total >= MAX_BUF_LEN){
                dzlog_error("out of the max buf");
                //cleanup
                total = 0;
                memset(buff, 0, sizeof(char)*MAX_BUF_LEN);
                bhas_header = false;
                p_header = nullptr;
                p_first_nonull = nullptr;
                continue;
            }
            buff[total] = '\0';

            dzlog_debug("recv buff:%s(total:%d--read:%d)", buff, total, nread);

            if(!bhas_header) {
                if (p_first_nonull == nullptr) {
                    bool is_ok = false;
                    for (int i = 0; i < total; i++) {
                        //find the first not null
                        if (buff[i] != '\0') {
                            p_first_nonull = buff + i;
                            dzlog_debug("the real buff(%d):%s", i, p_first_nonull);
                            is_ok = true;
                            break;
                        }

                    }
                    if(is_ok){
                        int a = *p_first_nonull;
                        if(a != 66){
                            dzlog_error("the data is invailed(%02X--%02X) , to reopen uart!!", a, *p_first_nonull);
                            bneed_reopen = true;
                            continue;
                        }
                        p_header = strstr(p_first_nonull, ITC_FRAME_HEADER);
                    }

                }else{
                    //TODO
                    p_header = strstr(p_first_nonull, ITC_FRAME_HEADER);
                    dzlog_info("there is no header and do the first nonull");
                }
            }

            if(p_header == nullptr){
                //没有
                if(bhas_header){
                    bhas_header = false;
                }
                dzlog_error("there is no frame header,continue to recv, current data len:%d,read:%d", total,nread);
                dzlog_error("there is no frame header,continue to recv, current data:%s", buff);
                if(nread >= 8){
                    //TODO??
                    dzlog_error("read >= 8 bytes, but not header, hope to reopen uart");
                    continue;
                }
                continue;
            }else{
                //如果有，判断有没有数据域
                if(!bhas_header){
                    bhas_header = true;
                    //TODO memmove
                }
                dzlog_debug("has the header...");
                int header_data_len = strlen(p_header);
                if(header_data_len >= 12){
                    //has data len
                    char data_len_str[5] = {0};
                    strncpy(data_len_str, p_header+8, 4);
                    long data_len = strtol(data_len_str, nullptr, 16);
                    printf("the data len is :%d\n", data_len);
                    //get the data
                    char *p_data = p_header+12;
                    long current_data_len = strlen(p_data);
                    printf("the current data len is :%d\n", current_data_len);
                    if(data_len >0 && (current_data_len < data_len)){
                        if((MAX_BUF_LEN-total) < data_len){
                            //TODO bakcup current data and clear the buff then reback to zhe buff
                            printf("the data buff is not enough\n");
                            //...
                            //...
                            continue;
                        }
                        //data is not complete, continue to read but with a timeout
                        dzlog_debug("the data is not completed, continue to recv:%s\n", buff);
                        continue;
                    }else{
                        //get the data
                        string asc_data_gbk;
                        if(data_len > 0){
                            char *data_buff = (char *)malloc(data_len+1);
                            memset(data_buff, 0, data_len+1);
                            strncpy(data_buff, p_data, data_len);
                            asc_data_gbk = data_buff;
                            free(data_buff);
                            data_buff = nullptr;

                            dzlog_debug("the data(GBK)  is :\n%s", asc_data_gbk.c_str());
                            string asc_data_utf8 = boost::locale::conv::between(asc_data_gbk, "UTF-8", "GBK" );//gbk-->utf-8
                            dzlog_debug("the data(UTF8) is :\n%s", asc_data_utf8.c_str());

                        }

                        char cmd[4] = {0};
                        strncpy(cmd, p_header+6, 2);
                        dzlog_info("\ntotal len:%d", total);
                        p_data[data_len] = '\0';
                        dzlog_info(">>> recv com data :");
                        dzlog_info("%s", p_header);
                        handle_com_event(0, cmd, asc_data_gbk);

                        //cleanup
                        total = 0;
                        memset(buff, 0, sizeof(char)*MAX_BUF_LEN);
                        bhas_header = false;
                        p_header = nullptr;
                        p_first_nonull = nullptr;
                        continue;
                    }
                }else{
                    printf("there is no data len,continue to recv, current data len:%d\n", total);
                    continue;
                }

            }
        }

    }
    free(buff);
    dzlog_info("the com thread is stopped...\n");

    return nullptr;
}


//#define COM_STDIN

int main(int argc, char *argv[])
{
    setup_signal_handlers();

    if(!initialize()){
        printf("init service error");
        cleanup();
        exit(5);
    }

    dzlog_info("SDHS ITC Com Server -- (V%s)", VERSION_NUM);
    dzlog_info("Startup... ...");

    g_dev_name = "/dev/ttyS0"; //串口一
    if(argc>=3)
    {
        if(strcmp(argv[1],"-o")==0)
        {
            g_dev_name = argv[2];
        }
    }

    dzlog_debug("will open dev :%s", g_dev_name.c_str());

#ifdef COM_STDIN

    char input_buff[MAX_BUF_LEN];

    char buff[MAX_BUF_LEN];
    memset(buff, 0, MAX_BUF_LEN);
    int total = 0;
    volatile bool bhas_header = false;
    char *p_header = nullptr;

    while (!quitting){
        printf("please input the test data from com:\n");
        ssize_t nread = 0;
        memset(input_buff, 0, MAX_BUF_LEN);
        char * ip = fgets(input_buff, MAX_BUF_LEN, stdin);
        nread = strlen(input_buff);
        input_buff[nread-1] = '\0';//erase the end of \n
        nread = strlen(input_buff);
        printf("this is the input data:%s:%d\n", input_buff, strlen(input_buff));

        if(nread < 0){
            break;
        }
        if(nread == EAGAIN || nread == 0){
            printf("continue to read :%ld\n", nread);
            continue;
        }
        if(nread > 0){
            total += nread;
            if(total >= MAX_BUF_LEN){
                printf("out of the max buf\n");
                //cleanup
                total = 0;
                memset(buff, 0, sizeof(char)*MAX_BUF_LEN);
                bhas_header = false;
                p_header = nullptr;
                continue;
            }
            buff[total] = '\0';
            if(total == nread){
                strcpy(buff, input_buff);
            }else{
                strcat(buff, input_buff);
            }

            if(!bhas_header){
                p_header = strstr(buff, ITC_FRAME_HEADER);
            }

            if(p_header == nullptr){
                //没有
                if(bhas_header){
                    bhas_header = false;
                }
                printf("there is no frame header,continue to recv, current data len:%d\n", total);
                continue;
            }else{
                //如果有，判断有没有数据域
                if(!bhas_header){
                    bhas_header = true;
                }
                int header_data_len = strlen(p_header);
                if(header_data_len > 12){
                    //has data len
                    char data_len_str[5] = {0};
                    strncpy(data_len_str, p_header+8, 4);
                    long data_len = strtol(data_len_str, nullptr, 16);
                    printf("the data len is :%d\n", data_len);
                    //get the data
                    char *p_data = p_header+12;
                    long current_data_len = strlen(p_data);
                    printf("the current data len is :%d\n", current_data_len);
                    if(current_data_len < data_len){
                        if((MAX_BUF_LEN-total) < data_len){
                            //TODO bakcup current data and clear the buff then reback to zhe buff
                            printf("the data buff is not enough\n");
                            //...
                            //...
                            continue;
                        }
                        //data is not complete, continue to read but with a timeout
                        printf("the data is not completed, continue to recv:%s\n", buff);
                        continue;
                    }else{
                        //get the data
                        char *data_buff = (char *)malloc(data_len+1);
                        memset(data_buff, 0, data_len+1);
                        strncpy(data_buff, p_data, data_len);
                        string asc_data_utf8 = data_buff;
//                        string asc_data_gbk = data_buff;
                        free(data_buff);
                        data_buff = nullptr;

                        string asc_data_gbk = boost::locale::conv::between(asc_data_utf8, "GBK", "UTF-8" );//utf-8-->gbk
                        printf("the data is :%s\n", asc_data_gbk.c_str());
                        char cmd[4] = {0};
                        strncpy(cmd, p_header+6, 2);
                        printf("\ntotal len:%d\n", total);
                        printf("total recv:%s\n", buff);
                        p_data[data_len] = '\0';
                        printf("the data frame is :%s\n", p_header);

                        handle_com_event(0, cmd, asc_data_gbk);
                        //memmove

                        //cleanup
                        total = 0;
                        memset(buff, 0, sizeof(char)*MAX_BUF_LEN);
                        bhas_header = false;
                        p_header = nullptr;
                    }
                }else{
                    printf("there is no data len,continue to recv, current data len:%d\n", total);
                    continue;
                }

            }
        }

    }
    cleanup();
#else
    int fd = 0;
    fd = open_uart(g_dev_name.c_str());
    if(fd < 0){
        dzlog_error("open uart %s failed : %d\n", g_dev_name.c_str(), fd);
        cleanup();
        exit(2);
    }
    process_mgr->com_fd = fd;

    int error = pthread_create(&process_mgr->master_tid,
                           NULL, /* default attributes please */
                           handle_com_thread,
                           NULL);
    if(error != 0){
        dzlog_error("create thread error, errno:%d\n", error);
        if(fd > 0){
            close_uart(fd);
        }
        cleanup();
        exit(3);
    }
    dzlog_info("create com thread ok...");

    pthread_join(process_mgr->master_tid, NULL);

    dzlog_info("clean up...");

    if(fd > 0){
        close_uart(fd);
    }
    cleanup();
    return 0;
#endif
}
